home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / What's New? / Software Development Kits / Translation Manager / Sample Code / Translation Extension Example / LowerToUpperCase.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-05  |  18.4 KB  |  592 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        LowerToUpperCase.c 
  3.  
  4.     Contains:    Source to a Translation Extension which converts from lower to upper case
  5.     
  6.     Copyright:    © 1994,1998 by Apple Computer, Inc., all rights reserved.
  7. */
  8.  
  9. #include <Types.h>
  10. #include <Files.h>
  11. #include <MixedMode.h>
  12. #include <Traps.h>
  13. #include <Errors.h>
  14. #include <Memory.h>
  15. #include <Resources.h>
  16. #include <Components.h>
  17. #include <TranslationExtensions.h>
  18.  
  19. #include "TranslationComponent.h"
  20. #include "LowerToUpperCase.h"
  21.  
  22. #include "string.h"
  23.  
  24. //___________________________________________________________________________________________________________
  25. //
  26. // Structures
  27. //
  28.  
  29. // Our magic cookie to tell if our translation list is initialized or not
  30. #define kModificationDateMagicCookie    0x00000001
  31.  
  32. // Set up a fixed scrap translation list structure for easy access
  33. #if defined(powerc) || defined (__powerc)
  34. #pragma options align=mac68k
  35. #endif
  36. struct OurScrapTranslationList
  37. {
  38.     unsigned long    modDate;
  39.     unsigned long    groupCount;
  40.     unsigned long    sourceCount;
  41.     unsigned long    sourceEntrySize;
  42.     ScrapTypeSpec    sourceType;
  43.     unsigned long    destinationCount;
  44.     unsigned long    destinationEntrySize;
  45.     ScrapTypeSpec    desinationType;
  46. };
  47. #if defined(powerc) || defined(__powerc)
  48. #pragma options align=reset
  49. #endif
  50.  
  51. typedef struct OurScrapTranslationList OurScrapTranslationList;
  52. typedef OurScrapTranslationList *OurScrapTranslationListPtr, **OurScrapTranslationListHandle;
  53.  
  54.  
  55. // Set up a fixed file translation list structure for easy access
  56. #if defined(powerc) || defined (__powerc)
  57. #pragma options align=mac68k
  58. #endif
  59. struct OurFileTranslationList
  60. {
  61.     unsigned long    modDate;
  62.     unsigned long    groupCount;
  63.     unsigned long    sourceCount;
  64.     unsigned long    sourceEntrySize;
  65.     FileTypeSpec    sourceType;
  66.     unsigned long    destinationCount;
  67.     unsigned long    destinationEntrySize;
  68.     FileTypeSpec    desinationType;
  69. };
  70. #if defined(powerc) || defined(__powerc)
  71. #pragma options align=reset
  72. #endif
  73.  
  74. typedef struct OurFileTranslationList OurFileTranslationList;
  75. typedef OurFileTranslationList *OurFileTranslationListPtr, **OurFileTranslationListHandle;
  76.  
  77.  
  78. //___________________________________________________________________________________________________________
  79. //
  80. // DoGetScrapTranslationList
  81. //
  82. // This routine is called by the Translation Manager when it needs to find out what formats the scrap
  83. // translator translates between.
  84. //
  85. // Enter:    self    Instance of this translator
  86. //            list    Handle to scrap translation list provided by this translator earlier, or empty handle
  87. //                    if never set before
  88. //
  89. // Exit:    list    Handle to scrap translation list filled in my this routine
  90. //            returns    Any errors that might occur
  91. //
  92. pascal ComponentResult DoGetScrapTranslationList(ComponentInstance             self, 
  93.                                                  ScrapTranslationListHandle    list)
  94. {
  95.     #pragma unused(self)
  96.     
  97.     OurScrapTranslationListHandle    ourList = (OurScrapTranslationListHandle) list;        // Easier to reference
  98.     OSErr                        result = noErr;
  99.  
  100.     // The list never changes here - so see if it's been initialized once by looking
  101.     // for our magic cookie
  102.     if (((*ourList)->modDate) != kModificationDateMagicCookie){
  103.         // Resize the handle to fit our entry
  104.         SetHandleSize((Handle)ourList, sizeof(OurScrapTranslationList));
  105.         if ((result = MemError()) == noErr) {
  106.             // Stuff all the values into our fixed list
  107.             (*ourList)->modDate = kModificationDateMagicCookie;                    // Magic cookie
  108.             (*ourList)->groupCount = 1;                                            // Number groups = 1
  109.             (*ourList)->sourceCount = 1;                                        // Only 1 source type
  110.             (*ourList)->sourceEntrySize = sizeof(ScrapTypeSpec);                // Currently always sizeof(ScrapTypeSpec)
  111.             (*ourList)->sourceType.format = 'TEXT';                                // Source type is always 'TEXT'
  112.             (*ourList)->sourceType.hint = 0;                                    // Don't care about this
  113.             (*ourList)->destinationCount = 1;                                    // Only 1 destination type
  114.             (*ourList)->destinationEntrySize = sizeof(ScrapTypeSpec);            // Currently always sizeof(ScrapTypeSpec)
  115.             (*ourList)->desinationType.format = 'UPPR';                            // Destination type is always 'UPPR'
  116.             (*ourList)->desinationType.hint = 0;                                // Don't care about this
  117.         }        
  118.     }
  119.  
  120.     return result;
  121. }
  122.  
  123. //___________________________________________________________________________________________________________
  124. //
  125. // DoIdentifyScrap
  126. //
  127. // This routine is called to identify a scraps contents.  In this case, just check to see if the scrap
  128. // is type TEXT, and if it is, then we know it's TEXT.
  129. //
  130. // Enter:    self        Instance of this translator
  131. //            dataPtr        Pointer to data to identify
  132. //            dataLength    Size of data referenced by dataPtr
  133. //            dataFormat    Provided format for the data referenced by dataPtr
  134. //
  135. // Exit:    dataFormat    Changed format (if applicable)
  136. //            returns        noErr is scrap contents are TEXT, otherwise noTypeErr
  137. //
  138. pascal ComponentResult DoIdentifyScrap(ComponentInstance    self, 
  139.                                        const void*             dataPtr, 
  140.                                        Size                 dataLength, 
  141.                                        ScrapType*             dataFormat)
  142. {
  143.     #pragma unused(self)
  144.     #pragma unused(dataPtr)
  145.     #pragma unused(dataLength)
  146.  
  147.     OSErr    result = noErr;
  148.     
  149.     // We only know TEXT.  See if the format is that.  If it isn't then we don't translate
  150.     if (*dataFormat != 'TEXT')
  151.         result = noTypeErr;
  152.  
  153.     return result;
  154. }
  155.  
  156. //___________________________________________________________________________________________________________
  157. //
  158. // DoTranslateScrap
  159. //
  160. // This routine is called to perform a scrap translation.  It is only called if the source type is TEXT and
  161. // if DoIdentifyScrap was called as was able to confirm that.  Also, it is only called if the destination type
  162. // is UPPR.
  163. //
  164. // This specific translation converts all lower case characters to upper case.  Leaves the rest alone.
  165. //
  166. // Enter:    self            Instance of this translator
  167. //            progressRefNum    Reference number to progress dialog
  168. //            srcDataPtr        Pointer to source data
  169. //            srcDataLength    Length of data pointed to by srcDataPtr
  170. //            srcType            Type of data pointed to by srcDataPtr (provided via DoIdentifyScrap)
  171. //            srcTypeHint        Hint for data pointed to by srcDataPtr (from ScrapTranslationList)
  172. //            dstData            Handle to place translated destination data into
  173. //            dstType            Type of data to translate srcDataPtr into
  174. //            dstTypeHint        Hint for data to translate to (from ScrapTranslationList)
  175. //
  176. // Exit:    dstData            Handle resized and filled in with translated contents
  177. //            returns            Any errors that might occur.
  178. //            
  179. pascal ComponentResult DoTranslateScrap(ComponentInstance     self, 
  180.                                         TranslationRefNum     progressRefNum,
  181.                                         const void*            srcDataPtr, 
  182.                                         Size                 srcDataLength, 
  183.                                         ScrapType             srcType, 
  184.                                         long                 srcTypeHint,
  185.                                         Handle                 dstData, 
  186.                                         ScrapType             dstType, 
  187.                                         long                 dstTypeHint)
  188. {
  189.     #pragma unused(srcType)
  190.     #pragma unused(srcTypeHint)
  191.     #pragma unused(dstType)
  192.     #pragma unused(dstTypeHint)
  193.  
  194.     OSErr    result;
  195.     char*    currentCharacter;
  196.     char*    lastCharacter;
  197.     char*    writeCharacter;
  198.     short    count;
  199.     Boolean    canceled = false;
  200.     short    myShort;
  201.     Handle  myAdvert;
  202.     OSErr    myErr;
  203.     
  204.     myShort = OpenComponentResFile((Component)self);
  205.     myAdvert = Get1Resource('PICT', kProgressAdvertisement);
  206.     if(myAdvert != nil){
  207.         DetachResource(myAdvert);
  208.         SetTranslationAdvertisement(progressRefNum,(PicHandle) myAdvert);
  209.     }
  210.     else {
  211.         SetTranslationAdvertisement(progressRefNum, nil);
  212.     }
  213.     myErr = CloseComponentResFile(myShort);
  214.     
  215.     // Resize the destination handle to fit all the translated contents
  216.     SetHandleSize(dstData, srcDataLength);
  217.     if ((result = MemError()) == noErr)
  218.     {
  219.         // Give me a pointer to the first character and the last character in the source buffer
  220.         currentCharacter = (char*) srcDataPtr;
  221.         lastCharacter = currentCharacter + srcDataLength;
  222.         
  223.         // Give me a pointer to the first character in the destination buffer
  224.         // (lock it down first because stupid update may move memory)
  225.         HLock(dstData);
  226.         writeCharacter = (char *)*dstData;
  227.         
  228.         // Keep a count for the progress dialog
  229.         count = 0;
  230.         
  231.         // Loop through each of the characters in the buffer
  232.         while ((currentCharacter <= lastCharacter) && !canceled)
  233.         {
  234.             // Are we in the lower case ASCII range?
  235.             if (('a' <= *currentCharacter) && (*currentCharacter <= 'z'))
  236.                 *writeCharacter = (*currentCharacter - ('a' - 'A'));
  237.             else
  238.                 *writeCharacter = *currentCharacter;
  239.             
  240.             // Increment the progress bar
  241.             UpdateTranslationProgress(progressRefNum, ++count * (100.00 / srcDataLength), &canceled);
  242.             
  243.             // Next…
  244.             currentCharacter++;
  245.             writeCharacter++;
  246.         }
  247.             
  248.         // Let it wiggle, see it squiggle
  249.         HUnlock(dstData);
  250.     }
  251.     
  252.     return result;
  253. }
  254.  
  255. //___________________________________________________________________________________________________________
  256. //
  257. // DoGetFileTranslationList
  258. //
  259. // This routine is called by the Translation Manager when it needs to find out what formats the scrap
  260. // translator translates between.
  261. //
  262. // Enter:    self    Instance of this translator
  263. //            list    Handle to file translation list provided by this translator earlier, or empty handle
  264. //                    if never set before
  265. //
  266. // Exit:    list    Handle to file translation list filled in my this routine
  267. //            returns    Any errors that might occur
  268. // 
  269. //
  270. pascal ComponentResult DoGetFileTranslationList(ComponentInstance             self, 
  271.                                                 FileTranslationListHandle    list)
  272. {
  273.     #pragma unused(self)
  274.  
  275.     OurFileTranslationListHandle    ourList = (OurFileTranslationListHandle) list;        // Easier to reference
  276.     OSErr                            result = noErr;
  277.  
  278.     // The list never changes here - so see if it's been initialized once by looking
  279.     // for our magic cookie
  280.     if (((*ourList)->modDate) != kModificationDateMagicCookie)
  281.     {
  282.         // Resize the handle to fit our entry
  283.         SetHandleSize((Handle)ourList, sizeof(OurFileTranslationList));
  284.         if ((result = MemError()) == noErr)
  285.          {
  286.             // Stuff all the values into our fixed list
  287.             (*ourList)->modDate = kModificationDateMagicCookie;                    // Magic cookie
  288.             (*ourList)->groupCount = 1;                                            // Number groups = 1
  289.             (*ourList)->sourceCount = 1;                                        // Only 1 source type
  290.             (*ourList)->sourceEntrySize = sizeof(FileTypeSpec);                    // Currently always sizeof(FileTypeSpec)
  291.             (*ourList)->sourceType.format = 'TEXT';                                // Source type is always 'TEXT'
  292.             (*ourList)->sourceType.hint = 0;                                    // Don't care about this
  293.             (*ourList)->sourceType.flags = 0;
  294.             (*ourList)->sourceType.catInfoType = 'TEXT';
  295.             (*ourList)->sourceType.catInfoCreator = 'ttxt';
  296.             (*ourList)->destinationCount = 1;                                    // Only 1 destination type
  297.             (*ourList)->destinationEntrySize = sizeof(FileTypeSpec);            // Currently always sizeof(FileTypeSpec)
  298.             (*ourList)->desinationType.format = 'UPPR';                            // Destination type is always 'UPPR'
  299.             (*ourList)->desinationType.hint = 0;                                // Don't care about this
  300.             (*ourList)->desinationType.flags = 0;
  301.             (*ourList)->desinationType.catInfoType = 'UPPR';
  302.             (*ourList)->desinationType.catInfoCreator = 'PPd√';                    //??
  303.         }        
  304.     }
  305.  
  306.         
  307.     return result;
  308. }
  309.  
  310. //___________________________________________________________________________________________________________
  311. //
  312. // DoIdentifyFile
  313. //
  314. // This routine is called to identify a file by its contents.  
  315. //
  316. // Enter:    self        Instance of this translator
  317. //            theDoc        file FSSpec
  318. //
  319. // Exit:    docKind        the fileType we decided it was
  320. //            returns        noErr 
  321. //
  322. //
  323. pascal ComponentResult DoIdentifyFile(ComponentInstance self, const FSSpec *theDoc, FileType *docKind)
  324. {
  325.     #pragma unused(self)
  326.     #pragma unused(theDoc)
  327.  
  328.     OSErr myErr = noErr;
  329.     
  330.     //not doing too much here
  331.     
  332.     *docKind = 'TEXT';
  333.     
  334.     return myErr;
  335. }
  336.  
  337. //___________________________________________________________________________________________________________
  338. //
  339. // DoTranslateFile
  340. //
  341. // This routine is called to perform a file translation.  It is only called if the file type is TEXT and
  342. // if DoIdentifyFile was called as was able to confirm that.  Also, it is only called if the destination type
  343. // is UPPR.
  344. //
  345. // This specific translation converts all lower case characters to upper case.  Leaves the rest alone.
  346. //
  347. // Enter:    self            Instance of this translator
  348. //            progressRefNum    Reference number to progress dialog
  349. //            srcDoc            Pointer to source file
  350. //            srcType            fileType provided from the DoIdentifyFile
  351. //            srcTypeHint        Hint for fileType pointed to by srcDoc (from GetTranslationList)
  352. //            dstDoc            Pointer to destination file spec
  353. //            dstType            file Type of destination file
  354. //            dstTypeHint        Hint for fileType to translate to (from GetTranslationList)
  355. //
  356. // Exit:    dstDoc            File is filled in with translation of data in source file
  357. //            returns            Any errors that might occur.
  358. //            
  359. //
  360. pascal ComponentResult DoTranslateFile(ComponentInstance self, TranslationRefNum progressRefNum,
  361.                                             const FSSpec* srcDoc, FileType srcType, long srcTypeHint, 
  362.                                             const FSSpec* dstDoc, FileType dstType, long dstTypeHint)
  363.     #pragma unused(srcType)
  364.     #pragma unused(srcTypeHint)
  365.     #pragma unused(dstType)
  366.     #pragma unused(dstTypeHint)
  367.  
  368.     OSErr     myErr = noErr;
  369.     short    srcRefNum, dstRefNum;
  370.     char    copyBuffer[512];
  371.     long    copyBufferSize;
  372.     Handle    myAdvert;
  373.     short    myShort;
  374.     
  375.     copyBufferSize = 512;
  376.     
  377.     myErr = FSpOpenDF( srcDoc, fsRdPerm, &srcRefNum ); 
  378.     if( myErr == noErr )
  379.     {
  380.         myErr = FSpOpenDF( dstDoc, fsRdWrPerm, &dstRefNum ); 
  381.         if( myErr == noErr )
  382.         {
  383.             myShort = OpenComponentResFile((Component)self);
  384.             myAdvert = Get1Resource('PICT', kProgressAdvertisement);
  385.             if(myAdvert != nil)
  386.             {
  387.                 DetachResource(myAdvert);
  388.                 SetTranslationAdvertisement(progressRefNum,(PicHandle) myAdvert);
  389.             }
  390.             else 
  391.                 SetTranslationAdvertisement(progressRefNum, nil);
  392.             
  393.             myErr = CloseComponentResFile(myShort);
  394.             myErr = CopyFork(*srcDoc, srcRefNum, dstRefNum, ©Buffer, copyBufferSize,progressRefNum );
  395.             
  396.         }
  397.     }
  398.     FSClose(srcRefNum);
  399.     FSClose(dstRefNum);
  400.     
  401.     return myErr;
  402. }
  403.  
  404. //___________________________________________________________________________________________________________
  405. //
  406. // DoGetTranslatedFilename
  407. //
  408. // 
  409. //
  410. pascal ComponentResult DoGetTranslatedFilename(ComponentInstance     self,
  411.                                                FileType                dstType,
  412.                                                long                    dstTypeHint,
  413.                                                FSSpec*                 theDocument)
  414. {
  415.     #pragma unused(self)
  416.     #pragma unused(dstType)
  417.     #pragma unused(dstTypeHint)
  418.     #pragma unused(theDocument)
  419.  
  420.     Str63 theString;
  421.     FSSpec mySpec;
  422.     OSErr  myErr = noErr;
  423.     short  i = 0;
  424.     
  425.     BlockMove( theDocument->name, theString, 64);
  426.     theDocument->name[0] = theDocument->name[0] + 3;
  427.     theDocument->name[1] = 'U';
  428.     theDocument->name[2] = 'U';
  429.     theDocument->name[3] = 'P';
  430.  
  431.     BlockMove( theString + 1, theDocument->name + 4, 60);
  432.     
  433.     myErr = FSMakeFSSpec( theDocument->vRefNum, theDocument->parID, theDocument->name, &mySpec);
  434.     //quick and dirty way of naming, some error checking should be done so no illegal characters are used
  435.     while( myErr != fnfErr ){
  436.         theDocument->name[3] = (char)(theDocument->name[3]) + 1;
  437.         myErr = FSMakeFSSpec( theDocument->vRefNum, theDocument->parID, theDocument->name, &mySpec);
  438.     }
  439.     
  440.     if( myErr == fnfErr ){
  441.         myErr = noErr;
  442.     }
  443.     
  444.     return myErr;
  445.     
  446. }
  447.  
  448.  
  449. pascal    OSErr    CopyFork(FSSpec srcSpec, short srcRefNum,
  450.                          short dstRefNum,
  451.                          void *copyBufferPtr,
  452.                          long copyBufferSize, TranslationRefNum progressRefNum)
  453. {
  454.     ParamBlockRec srcPB;
  455.     ParamBlockRec dstPB;
  456.     OSErr srcError;
  457.     OSErr dstError;
  458.     Boolean canceled = false;
  459.     short    myPercent = 0;
  460.     short    yourPercent;
  461.     unsigned short  numBlocks;
  462.     long    myBlockSize;
  463.     
  464.     char*    currentCharacter;
  465.     char*    lastCharacter;
  466.  
  467.     if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) )
  468.         return ( paramErr );
  469.     
  470.     srcPB.ioParam.ioRefNum = srcRefNum;
  471.     dstPB.ioParam.ioRefNum = dstRefNum;
  472.  
  473.     // preallocate the destination fork and 
  474.     // ensure the destination fork's EOF is correct after the copy 
  475.     srcError = PBGetEOFSync(&srcPB);
  476.     if ( srcError != noErr )
  477.         return ( srcError );
  478.     dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc;
  479.     dstError = PBSetEOFSync(&dstPB);
  480.     if ( dstError != noErr )
  481.         return ( dstError );
  482.     
  483.     // If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes 
  484.     // This will make writes on local volumes faster
  485.     
  486.     myBlockSize = ((copyBufferSize >= 512) && (copyBufferSize % 512)) ?
  487.                                (copyBufferSize / 512) * 512 :
  488.                                (copyBufferSize);
  489.     
  490.     //need to know the size of the file to give user feedback 
  491.     //thru the progress dialog box. So here we count the blocks
  492.     
  493.     srcError = CountTheBlocks(srcSpec.vRefNum,
  494.                                        srcSpec.parID,
  495.                                        srcSpec.name, myBlockSize,
  496.                                        &numBlocks);    
  497.     if( srcError == noErr ){
  498.         myPercent = numBlocks;
  499.     }
  500.     else{
  501.         myPercent = 0;
  502.     }
  503.  
  504.     // reset source fork's mark 
  505.     srcPB.ioParam.ioPosMode = fsFromStart;
  506.     srcPB.ioParam.ioPosOffset = 0;
  507.     srcError = PBSetFPosSync(&srcPB);
  508.     if ( srcError != noErr )
  509.         return ( srcError );
  510.  
  511.     // reset destination fork's mark 
  512.     dstPB.ioParam.ioPosMode = fsFromStart;
  513.     dstPB.ioParam.ioPosOffset = 0;
  514.     dstError = PBSetFPosSync(&srcPB);
  515.     if ( dstError != noErr )
  516.         return ( dstError );
  517.  
  518.     // set up fields that won't change in the loop 
  519.     srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  520.     srcPB.ioParam.ioPosMode = fsAtMark + 0x0020;// fsAtMark + noCacheBit 
  521.     srcPB.ioParam.ioReqCount = myBlockSize;
  522.  
  523.     dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  524.     dstPB.ioParam.ioPosMode = fsAtMark + 0x0020;// fsAtMark + noCacheBit 
  525.     yourPercent = 0;
  526.     while ( (srcError == noErr) && (dstError == noErr) )
  527.     {
  528.         srcError = PBReadSync(&srcPB);
  529.         dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount;
  530.         
  531.         currentCharacter = (char*)copyBufferPtr;
  532.         lastCharacter = currentCharacter + srcPB.ioParam.ioActCount;
  533.         
  534.         while ((currentCharacter <= lastCharacter) && !canceled)
  535.         {
  536.             // Are we in the lower case ASCII range?
  537.             if (('a' <= *currentCharacter) && (*currentCharacter <= 'z'))
  538.             {
  539.                 *currentCharacter = (*currentCharacter - ('a' - 'A'));
  540.             }
  541.             
  542.             // Next…
  543.             currentCharacter++;
  544.         }
  545.             
  546.         
  547.         dstError = PBWriteSync(&dstPB);
  548.         yourPercent++;
  549.         UpdateTranslationProgress(progressRefNum, ((yourPercent * 100)/myPercent) , &canceled);
  550.         
  551.     }
  552.  
  553.     // make sure there were no errors at the destination 
  554.     if ( dstError != noErr )
  555.         return ( dstError );
  556.  
  557.     // make sure the only error at the source was eofErr 
  558.     if ( srcError != eofErr )
  559.         return ( srcError );
  560.  
  561.     return ( noErr );
  562. }
  563.  
  564.  
  565. static    OSErr    CountTheBlocks(short srcVRefNum,
  566.                                        long srcDirID,
  567.                                        ConstStr255Param srcName, long theBlockSize,
  568.                                        unsigned short *numDataBlks)
  569. {
  570.     HParamBlockRec pb;
  571.     OSErr error;
  572.     unsigned short srcDataBlks;
  573.     
  574.     // get the size of the file's data resource fork 
  575.     pb.fileParam.ioNamePtr = (StringPtr)srcName;
  576.     pb.fileParam.ioVRefNum = srcVRefNum;
  577.     pb.fileParam.ioFVersNum = 0;
  578.     pb.fileParam.ioDirID = srcDirID;
  579.     pb.fileParam.ioFDirIndex = 0;
  580.     error = PBHGetFInfoSync(&pb);
  581.     if ( error == noErr )
  582.     {
  583.     // get number of 512-byte blocks needed for data fork 
  584.     srcDataBlks = ((unsigned long)pb.fileParam.ioFlLgLen % 512) ?
  585.                   (((unsigned long)pb.fileParam.ioFlLgLen >> 9) + 1) :
  586.                   ((unsigned long)pb.fileParam.ioFlLgLen >> 9);
  587.     }
  588.     
  589.     *numDataBlks = ( srcDataBlks *  512 )/ theBlockSize;
  590.     return ( error );
  591. }